﻿using System;
using System.Collections.Generic;
using System.Threading;
using System.Text;

namespace DiceWars
{
	internal class DiceWar
	{
		internal delegate bool PlayerStrategyFunc(Player P, DiceWar D, params object[] parameters);
		internal const int MAX_STACK = 8;
		internal const int MAX_RESERVED = 3200;
		internal const int MAX_ADD = 640;
		internal Random R;

		internal List<Player> Players = new List<Player>();
		internal Graph<V3D> Territories = new Graph<V3D>();
		internal List<Territory> TList = new List<Territory>();
		internal int Round = 1;

		internal DiceWar(int seed = -1)
		{
			if (seed == -1)
				R = new Random();
			else
				R = new Random(seed);
		}

		internal void CreatePlayer(String n, V3D col)
		{
			Players.Add(new Player(n, col));
		}
		
		internal Territory CreateTerritory(V3D Pos, int P, int D)
		{
			Territory Res = new Territory(Pos);
			Res.NumDice = D;
			Res.Owner = Players[P];
			Territories.Points.Add(Res);
			TList.Add(Res);
			return Res;
		}

		bool Immediate = true;

		public void CalculateAreas()
		{
			foreach (var P in Territories.Points)
				P.Dij_Group = Players.IndexOf((P as Territory).Owner);
			Territories.CalculateAreas();
			foreach (Player P in Players)
				if (Territories.MaxAreas.ContainsKey(Players.IndexOf(P)))
					P.MaxTerr = Territories.MaxAreas[Players.IndexOf(P)].Dij_Size;
		}
		public void CalculatePlayers()
		{
			foreach (var P in Players)
			{
				P.NumDice = 0;
				P.NumTerr = 0;
				P.MaxTerr = 0;
			}
			foreach (var a in Territories.Areas)
			{
				Players[a.Key].MaxTerr = Math.Max(Players[a.Key].MaxTerr, a.Value.Dij_Size);
				Players[a.Key].NumTerr += a.Value.Dij_Size;
			}
			foreach (var t in Territories.Points)
				(t as Territory).Owner.NumDice += (t as Territory).NumDice;
		}
		public void UpdateStat()
		{
			CalculateAreas();
			CalculatePlayers();
		}
		public void Resolve(Player P)
		{
			UpdateStat();
			P.Reserved += Math.Min(MAX_ADD, P.MaxTerr);
			int Adding = Math.Min(P.Reserved, DiceWar.MAX_RESERVED);
			//Console.WriteLine(P.NumDice);
			//Console.WriteLine(Adding);

			List<Territory> owned = TList.FindAll(x => x.Owner == P && x.NumDice < DiceWar.MAX_STACK);
			while (Adding > 0)
			{
				if (owned.Count == 0)
				{
					P.Reserved = Adding;
					Adding = 0;
				}
				else
				{
					int r = R.Next(0, owned.Count);
					owned[r].NumDice++;
					if (owned[r].NumDice == MAX_STACK)
						owned.RemoveAt(r);
					Adding--;
					if (Adding == 0)
						P.Reserved = 0;
				}
			}

		}
		void PlayCore(object player)
		{
			if (Players.Count == 1)
				return;
			Player P = player as Player;
			bool Moving = true;
			while (Moving)
			{
				Moving = AI1(P, this);
			}
			Resolve(P);
		}
		void PlayCore(Player player,PlayerStrategyFunc Func)
		{
			if (Players.Count == 1)
				return;
			Player P = player as Player;
			bool Moving = true;
			while (Moving)
			{
				Moving = Func(P, this, 2);
			}
			Resolve(P);
		}

		static bool AI1(Player P, DiceWar D, params object[] parameters)
		{
			List<Territory> Res = new List<Territory>();
			Dictionary<Territory, Territory> Attacks = new Dictionary<Territory, Territory>();

			List<Territory> TL = D.TList;
			if (parameters.Length > 1 && parameters[1] is Territory[])
				TL = new List<Territory>(parameters[1] as Territory[]);
			else
				TL = TL.FindAll(t => t.Owner == P && t.NumDice > 1);
			TL.ForEach(x =>
			{
				foreach (var a in x.Adjacents)
					if ((a.Key as Territory).Owner != x.Owner && (a.Key as Territory).NumDice <= x.NumDice + 8)
					{
						if (Attacks.ContainsKey(a.Key as Territory))
						{
							if ((Attacks[a.Key as Territory] as Territory).NumDice < x.NumDice)
								Attacks[a.Key as Territory] = x;
						}
						else
							Attacks.Add(a.Key as Territory, x);
					}
			});
			foreach (var X in Attacks)
			{
				if (X.Value.Attack(X.Key, D.R))
					Res.Add(X.Key);
			}
			return Attacks.Count > 0;
		}
		public static List<Territory> AI2(Player P, DiceWar D, params object[] parameters)
		{
			List<Territory> Res = new List<Territory>();
			Dictionary<Territory, Territory> Attacks = new Dictionary<Territory, Territory>();

			List<Territory> TL = D.TList;
			if (parameters.Length > 1 && parameters[1] is Territory[])
				TL = new List<Territory>(parameters[1] as Territory[]);
			else
				TL = TL.FindAll(t => t.Owner == P && t.NumDice > 1);
			TL.ForEach(x =>
			{
				foreach (var a in x.Adjacents)
					if ((a.Key as Territory).Owner != x.Owner && (a.Key as Territory).NumDice <= x.NumDice)
					{
						if (Attacks.ContainsKey(a.Key as Territory))
						{
							if ((Attacks[a.Key as Territory] as Territory).NumDice < x.NumDice)
								Attacks[a.Key as Territory] = x;
						}
						else
							Attacks.Add(a.Key as Territory, x);
					}
			});
			foreach (var X in Attacks)
			{
				if (X.Value.Attack(X.Key, D.R))
					Res.Add(X.Key);
			}
			return Res;
		}
		internal void Play(Player P)
		{
			Immediate = false;
			new Thread(new ParameterizedThreadStart(PlayCore)).Start(P);
		}
		internal void PlayImmediate(Player P)
		{
			Immediate = true;
			PlayCore(P);
		}
		internal void PlayImmediate(Player P, PlayerStrategyFunc F)
		{
			Immediate = true;
			PlayCore(P, F);
		}
	}
	internal class Territory : GraphPoint<V3D>
	{
		internal Player Owner;
		internal int NumDice;

		internal List<OSM.OSMWay> OwnBorder = new List<OSM.OSMWay>();

		internal void SetOwnerAndDice(Player P, int D)
		{
			Owner = P;
			NumDice = D;
		}
		internal Territory(V3D P)
			: base(P)
		{
		}
		internal int Size = 1;
		internal bool Marked = false;

		internal bool Attack(Territory Target, Random R)
		{
			String Res = "";
			if (NumDice == 1 || Target.Owner == Owner)
				return false;
			int N1 = 0;
			int N2 = 0;
			for (int i = 0; i < NumDice; i++)
			{
				int d = R.Next(1, 7);
				Res += (i == 0 ? "" : "-") + d;
				N1 += d;
			}
			Res += ": " + N1.ToString("00") + " ";
			for (int i = 0; i < Target.NumDice; i++)
			{
				int d = R.Next(1, 7);
				Res += (i == 0 ? "" : "-") + d;
				N2 += d;
			}
			Res += ": " + N2.ToString("00");
			//Console.WriteLine(Res);
			if (N1 > N2)
			{
				Target.Owner = Owner;
				Target.NumDice = NumDice - 1;
				NumDice = 1;
				return true;
			}
			else
			{
				NumDice = 1;
				return false;
			}
		}
	}
	internal class DiceBorder : GraphEdge<V3D>
	{
		internal double[] edge;
		internal int StartID, EndID;
		internal OSM.OSMWay Way;
		public DiceBorder(Territory T1, Territory T2, OSM.OSMWay W)
			: base(T1, T2, 1)
		{
			Way = W;
		}
	}
	internal class Player
	{
		internal V3D Color;
		internal string Name;

		internal int NumTerr = 0;
		internal int NumDice = 0;
		internal int MaxTerr = 0;
		internal int Reserved = 0;
		public override string ToString()
		{
			return "S: " + MaxTerr + " D: " + NumDice + " T: " + NumTerr;
		}
		internal Player(string name, V3D col)
		{
			Name = name;
			Color = col;
		}
	}
}